import { SetupContext, computed, ref } from "vue";
import { ConditionProps } from "../condition.props";
import { UseCondition, UseSelection } from "./types";
import { ConditionGroup, Condition } from "../types";

export function useSelection(
    props: ConditionProps,
    context: SetupContext,
    useConditionComposition: UseCondition
): UseSelection {
    const selectedItems = ref(new Set<number>());
    const selectedGroups = ref(new Set<number>());
    const { conditionGroupMap, groupParentMap } = useConditionComposition;
    let ownerGroup: ConditionGroup;

    let unSelectItem: (condition: Condition) => void;

    function getGroupsToSelect(selectingGroup: ConditionGroup) {
        let topGroup = ownerGroup.level <= selectingGroup.level ? ownerGroup : selectingGroup;
        let seekingGroup = ownerGroup.level > selectingGroup.level ? ownerGroup : selectingGroup;
        const seekingPath = [];
        while (seekingGroup && topGroup && seekingGroup !== topGroup) {
            if (seekingGroup.level > topGroup.level) {
                seekingPath.push(seekingGroup);
                seekingGroup = groupParentMap.get(seekingGroup.groupId) as ConditionGroup;
            } else if (seekingGroup.level === topGroup.level) {
                topGroup = groupParentMap.get(topGroup.groupId) as ConditionGroup;
                if (topGroup) {
                    seekingPath.unshift(topGroup);
                }
            }
        }
        return seekingPath;
    }

    function selectConditionGroup(conditionGroup: ConditionGroup) {
        selectedGroups.value.add(conditionGroup.groupId);
        conditionGroup.items.forEach((condition: Condition) => selectedItems.value.add(condition.conditionId));
        conditionGroup.children.forEach((subConditionGroup: ConditionGroup) => selectConditionGroup(subConditionGroup));
    }

    function selectItem(condition: Condition) {
        const { conditionId } = condition;
        selectedItems.value.add(conditionId);
        ownerGroup = selectedItems.value.size === 0 ? conditionGroupMap.get(conditionId) as ConditionGroup : ownerGroup;
        const selectingGroup = conditionGroupMap.get(condition.conditionId) as ConditionGroup;
        const groupsToSelect = getGroupsToSelect(selectingGroup);
        groupsToSelect.forEach((selectedGroup: ConditionGroup) => selectConditionGroup(selectedGroup));
    }

    const shouldKeepSelectedLastConditionGroup = computed(() => selectedItems.value.size === 0 && selectedGroups.value.size === 1);

    const unSelectConditionGroup = function (conditionGroup: ConditionGroup) {
        selectedGroups.value.delete(conditionGroup.groupId);
        conditionGroup.items.forEach((condition: Condition) => unSelectItem(condition));
        conditionGroup.children.forEach((subConditionGroup: ConditionGroup) =>
            !shouldKeepSelectedLastConditionGroup.value && unSelectConditionGroup(subConditionGroup));
        const parentGroup = groupParentMap.get(conditionGroup.groupId);
        if (parentGroup && selectedGroups.value.has(parentGroup.groupId)) {
            unSelectConditionGroup(parentGroup);
        }
    };

    unSelectItem = function (condition: Condition) {
        const { conditionId } = condition;
        if (selectedItems.value.has(conditionId)) {
            selectedItems.value.delete(conditionId);
        }
        if (selectedGroups.value.size > 0) {
            const conditionGroup = conditionGroupMap.get(conditionId);
            if (conditionGroup && selectedGroups.value.has(conditionGroup.groupId)) {
                unSelectConditionGroup(conditionGroup);
            }
        }
    };

    function toggleSelect(condition: Condition) {
        const { conditionId } = condition;
        if (selectedItems.value.size === 0) {
            ownerGroup = conditionGroupMap.get(conditionId) as ConditionGroup;
        }
        if (selectedItems.value.has(conditionId)) {
            unSelectItem(condition);
        } else {
            selectItem(condition);
        }
    }

    function getSelectedGroupItem() {
        const items: (Condition | ConditionGroup)[] = [];
        if (ownerGroup) {
            ownerGroup.items
                .filter((queryCondition: Condition) => selectedItems.value.has(queryCondition.conditionId))
                .forEach((selectedItem: Condition) => items.push(selectedItem));
            ownerGroup.children
                .filter((conditionGroup: ConditionGroup) => selectedGroups.value.has(conditionGroup.groupId))
                .forEach((selectedGroup: ConditionGroup) => items.push(selectedGroup));
        }
        return items;
    }

    function clear() {
        selectedItems.value.clear();
        selectedGroups.value.clear();
    }

    return { clear, getSelectedGroupItem, selectedItems, toggleSelect };

}
